home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
pc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-28
|
21KB
|
945 lines
/* OS- and machine-dependent stuff for IBM-PC running MS-DOS and Turbo-C
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#include <conio.h>
#include <dir.h>
#include <dos.h>
#include <io.h>
#include <sys/stat.h>
#include <string.h>
#include <process.h>
#include <fcntl.h>
#include <alloc.h>
#include <stdarg.h>
#include <bios.h>
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "internet.h"
#include "session.h"
#include "socket.h"
#include "smtp.h"
#include "cmdparse.h"
#include "dirutil.h"
#include "pc.h"
#define CTLC 0x3
#define DEL 0x7f
static int kbchar __ARGS((void));
extern int Curdisp;
extern struct proc *Display;
FILE *Rawterm;
unsigned _stklen = 8192;
int Tick;
static int32 Starttime;
int32 Clock;
int Background = 1;
int Watchdog = 0; /* Watch Dog off by default */
int WDTick = 300*(1000 / MSPTICK); /* 5 minutes watchdog timer */
int WDCurr = 300*(1000 / MSPTICK); /* Initial count down timer */
/* This flag is set by setirq() if IRQ 8-15 is used, indicating
* that the machine is a PC/AT with a second 8259 interrupt controller.
* If this flag is set, the interrupt return code in pcgen.asm will
* send an End of Interrupt command to the second 8259 as well as the
* first.
*/
int Isat;
static char Ttbuf[BUFSIZ];
static char Tsbuf[BUFSIZ];
static int saved_break;
/* Keyboard input buffer */
#define KBSIZE 256
static struct {
char buf[KBSIZE];
char *wp;
char *rp;
int cnt;
} Keyboard;
int Nokeys = 0;
int
am_i_an_AT()
{
unsigned char *model_code = MK_FP(0xF000,0xFFFE);
if(*model_code == 0xFC)
return 1;
else
return 0;
}
int
errhandler(errval,ax,bp,si)
int errval,ax,bp,si;
{
return 3; /* Fail the system call */
}
/* Called at startup time to set up console I/O, memory heap */
void
ioinit()
{
/* Fail all I/O errors */
harderr(errhandler);
/* Save these two file table entries for something more useful */
fclose(stdaux);
fclose(stdprn);
setbuf(stdout,Tsbuf);
Rawterm = fopen("con","wb");
setbuf(Rawterm,Ttbuf);
/* this breaks tab expansion so you must use ANSI or NANSI */
ioctl(fileno(Rawterm), 1, (ioctl(fileno(Rawterm),0) & 0xff) | 0x20);
saved_break = getcbrk();
setcbrk(0);
#ifdef MSDOS
/* test to see if we're running on an AT class machine.
* Set Isat flag accordingly - N1BEE
*/
Isat = am_i_an_AT();
#endif
Starttime = bioscnt();
/* Link timer handler into timer interrupt chain */
chtimer(btick);
/* Find out what multitasker we're running under, if any */
chktasker();
/* Initialize keyboard queue */
Keyboard.rp = Keyboard.wp = Keyboard.buf;
}
/* Called just before exiting to restore console state */
void
iostop()
{
struct iface *ifp,*iftmp;
void (**fp)();
setbuf(Rawterm,NULLCHAR);
ioctl(fileno(Rawterm), 1, ioctl(fileno(Rawterm), 0) & 0xff & ~0x20);
setcbrk(saved_break);
for(ifp = Ifaces;ifp != NULLIF;ifp = iftmp){
iftmp = ifp->next;
if_detach(ifp);
}
/* Call list of shutdown functions */
for(fp = Shutdown;*fp != NULLVFP;fp++){
(**fp)();
}
}
/* Spawn subshell */
int
doshell(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *command;
int ret;
if(Background) {
if(!start_back())
return -1;
Nokeys++;
}
if(argc == 1 || !stricmp(argv[1], "/c")) {
if((command = getenv("COMSPEC")) == NULLCHAR)
command = "COMMAND.COM";
ret = spawnvp(P_WAIT,command,argv);
} else {
ret = spawnvp(P_WAIT,argv[1],(argv + 1));
}
if(Background) {
Nokeys--;
stop_back();
}
return ret;
}
/* Spawn mailer as subshell */
int
dobmail(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *command;
int ret;
if(Background) {
if(!start_back())
return -1;
Nokeys++;
}
if((command = getenv("MAILER")) == NULLCHAR)
command = "BM.EXE";
ret = spawnvp(P_WAIT,command,argv);
if(Background) {
Nokeys--;
stop_back();
}
smtptick(NULL); /* tickle smtp to send any mail */
return ret;
}
/* if multitask mode is set - allow NOS and shell/mail to share system time */
dobackg(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setbool(&Background,"Multitasking DOS Shell ",argc,argv);
}
/* if watch-dog mode is set - make NOS reboot the system if it stalls */
dowatchdog(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setbool(&Watchdog,"NOS Watch Dog",argc,argv);
}
/* Keyboard interrupt handler */
void
kbint()
{
int sig = 0;
int c;
if(Background && Nokeys)
return;
while((c = kbraw()) != -1 && Keyboard.cnt < KBSIZE){
sig = 1;
*Keyboard.wp++ = c;
if(Keyboard.wp == &Keyboard.buf[KBSIZE])
Keyboard.wp = Keyboard.buf;
Keyboard.cnt++;
}
if(sig){
psignal(&Keyboard,0);
}
}
static int
kbchar()
{
char i_state;
char c;
if(Background && Nokeys)
return -1;
i_state = dirps();
while(Keyboard.cnt == 0)
pwait(&Keyboard);
Keyboard.cnt--;
restore(i_state);
c = *Keyboard.rp++;
if(Keyboard.rp == &Keyboard.buf[KBSIZE])
Keyboard.rp = Keyboard.buf;
return uchar(c);
}
/* Flush the raw terminal output */
void
rflush()
{
fflush(Rawterm);
}
#ifdef MSDOS
struct funcstr {
char fkey;
char alloced;
char *fvalue;
};
struct funcstr fkeys[] = {
15,0,NULLCHAR, /* tab + shift */
59,1,"\033OP", /* F1 */
60,1,"\033OQ", /* F2 */
61,1,"\033OR", /* F3 */
62,1,"\033OS", /* F4 */
63,0,NULLCHAR, /* F5 */
64,0,NULLCHAR, /* F6 */
65,0,NULLCHAR, /* F7 */
66,0,NULLCHAR, /* F8 */
67,0,NULLCHAR, /* F9 */
68,0,NULLCHAR, /* F10 */
71,1,"\010", /* home*/
72,1,"\033[A", /* up arrow*/
73,1,"\025", /* pgup */
75,1,"\033[D", /* left arrow */
77,1,"\033[C", /* right arrow */
79,1,"\005", /* end */
80,1,"\033[B", /* down arrow */
81,1,"\012", /* pgdn */
82,1,"\001", /* ins */
83,1,"\0177", /* del */
84,0,NULLCHAR, /* F1 + shift*/
85,0,NULLCHAR, /* F2 + shift*/
86,0,NULLCHAR, /* F3 + shift*/
87,0,NULLCHAR, /* F4 + shift*/
88,0,NULLCHAR, /* F5 + shift*/
89,0,NULLCHAR, /* F6 + shift*/
90,0,NULLCHAR, /* F7 + shift*/
91,0,NULLCHAR, /* F8 + shift*/
92,0,NULLCHAR, /* F9 + shift*/
93,0,NULLCHAR, /* F10 + shift*/
94,0,NULLCHAR, /* F1 + control*/
95,0,NULLCHAR, /* F2 + control*/
96,0,NULLCHAR, /* F3 + control*/
97,0,NULLCHAR, /* F4 + control*/
98,0,NULLCHAR, /* F5 + control*/
99,0,NULLCHAR, /* F6 + control*/
100,0,NULLCHAR, /* F7 + control*/
101,0,NULLCHAR, /* F8 + control*/
102,0,NULLCHAR, /* F9 + control*/
103,0,NULLCHAR, /* F10 + control*/
104,0,NULLCHAR, /* F1 + alt*/
105,0,NULLCHAR, /* F2 + alt*/
106,0,NULLCHAR, /* F3 + alt*/
107,0,NULLCHAR, /* F4 + alt*/
108,0,NULLCHAR, /* F5 + alt*/
109,0,NULLCHAR, /* F6 + alt*/
110,0,NULLCHAR, /* F7 + alt*/
111,0,NULLCHAR, /* F8 + alt*/
112,0,NULLCHAR, /* F9 + alt*/
113,0,NULLCHAR, /* F10 + alt*/
114,0,NULLCHAR, /* PrtSc + ctl*/
117,0,NULLCHAR, /* end + ctl */
118,0,NULLCHAR, /* pgup + ctl */
119,0,NULLCHAR, /* home + ctl */
132,0,NULLCHAR, /* pgdn + ctl */
0,0,NULLCHAR
};
char Leftover = 0;
char *Nextkey;
#endif
/* Read characters from the keyboard, translating them to "real" ASCII.
* If none are ready, block. The F-10 key is special; translate it to -2.
*/
int
kbread()
{
#ifndef MSDOS
int c;
#else
int c,i,j;
if((c = Leftover) != 0) {
Leftover = *Nextkey++;
return c;
}
#endif MSDOS
if((c = kbchar()) == 0){
/* Lead-in to a special char */
c = kbchar();
switch(c){
case 3: /* NULL (bizzare!) */
c = 0;
break;
case 68: /* F-10 key (used as command-mode escape) */
if(fkeys[10].fvalue == NULLCHAR){
c = -2;
break;
}
default: /* Dunno what it is */
#ifdef MSDOS
for(i=0;(j = fkeys[i].fkey) != 0;i++)
if(j == c) {
Nextkey = fkeys[i].fvalue;
if(Nextkey == NULLCHAR) {
c = -1;
return c;
}
if((c = *Nextkey++) != 0)
Leftover = *Nextkey++;
else
c = -1;
return c;
}
#endif
c = -1;
}
}
return c;
}
#ifdef MSDOS
int
dofkey(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int c,i,j;
char *q, *r;
char str[100];
if(argc == 1) {
tprintf("key num key num key num key num key num\n");
tprintf("f1 59 sf1 84 cf1 94 af1 104 pgup 73\n");
tprintf("f2 60 sf2 85 cf2 95 af2 105 pgdn 81\n");
tprintf("f3 61 sf3 86 cf3 96 af3 106 home 71\n");
tprintf("f4 62 sf4 87 cf4 97 af4 107 end 79\n");
tprintf("f5 63 sf5 88 cf5 98 af5 108 arup 72\n");
tprintf("f6 64 sf6 89 cf6 99 af6 109 ardn 80\n");
tprintf("f7 65 sf7 90 cf7 100 af7 110 ar l 75\n");
tprintf("f8 66 sf8 91 cf8 101 af8 111 ar r 77\n");
tprintf("f9 67 sf9 92 cf9 102 af9 112 ins 82\n");
tprintf("f10 68 sf10 93 cf10 103 af10 113 del 83\n");
tprintf("usage: fkey <key number> [<value> | \"string\"]\n");
return 0;
}
c = atoi(argv[1]);
if(c == 0 || c > 255) {
tprintf("fkey number out of range.\n");
return 1;
}
for(j = 0;(i = fkeys[j].fkey) != 0; j++)
if(i == c)
break;
if(i == 0){
tprintf("fkey number not found\n");
return 1;
}
if(argc == 2) {
q = fkeys[j].fvalue;
r = str;
if(q == NULLCHAR)
tprintf("fkey %d has no assigned value.\n",c);
else {
while(*q)
if(*q < ' ') { /* This is ASCII dependent !! */
*r++ = '^';
*r++ = *q++ + 0x40;
} else
*r++ = *q++;
*r = '\0';
tprintf("fkey = %s\n",str);
}
return 0;
}
if(argc == 3) {
if(fkeys[j].alloced)
fkeys[j].alloced = 0;
else
if(fkeys[j].fvalue != NULLCHAR)
free(fkeys[j].fvalue);
r = str;
q = argv[2];
while(*q){
if(*q == '^'){ /* ^ gives control char next */
q++;
if(*q == '^') {
*r++ = *q++; /* No, he wants a ^ */
} else {
*r++ = *q++ & 0x1f;
}
} else
*r++ = *q++;
}
*r = '\0';
fkeys[j].fvalue = strdup(str);
}
return 0;
}
#endif
/* Install hardware interrupt handler.
* Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
* Note that bus line IRQ2 maps to IRQ9 on the AT
*/
int
setirq(irq,handler)
unsigned irq;
INTERRUPT (*handler)();
{
/* Set interrupt vector */
if(irq < 8){
setvect(8+irq,handler);
} else if(irq < 16){
Isat = 1;
setvect(0x70 + irq - 8,handler);
} else {
return -1;
}
return 0;
}
/* Return pointer to hardware interrupt handler.
* Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
*/
INTERRUPT
(*getirq(irq))()
unsigned int irq;
{
/* Set interrupt vector */
if(irq < 8){
return getvect(8+irq);
} else if(irq < 16){
return getvect(0x70 + irq - 8);
} else {
return NULLVIFP;
}
}
/* Disable hardware interrupt */
int
maskoff(irq)
unsigned irq;
{
if(irq < 8){
setbit(0x21,(char)(1<<irq));
} else if(irq < 16){
irq -= 8;
setbit(0xa1,(char)(1<<irq));
} else {
return -1;
}
return 0;
}
/* Enable hardware interrupt */
int
maskon(irq)
unsigned irq;
{
if(irq < 8){
clrbit(0x21,(char)(1<<irq));
} else if(irq < 16){
irq -= 8;
clrbit(0xa1,(char)(1<<irq));
} else {
return -1;
}
return 0;
}
/* Return 1 if specified interrupt is enabled, 0 if not, -1 if invalid */
int
getmask(irq)
unsigned irq;
{
if(irq < 8)
return (inportb(0x21) & (1 << irq)) ? 0 : 1;
else if(irq < 16){
irq -= 8;
return (inportb(0xa1) & (1 << irq)) ? 0 : 1;
} else
return -1;
}
/* Called from assembler stub linked to BIOS interrupt 1C, called on each
* hardware clock tick. Signal a clock tick to the timer process.
*/
void
ctick()
{
if(Watchdog)
if(WDCurr-- == 0)
sysreset();
Tick++;
#ifdef notdef
Clock++; /* Keep system time */
#endif
psignal(&Tick,1);
}
/* Called from the timer process on every tick. NOTE! This function
* can NOT be called at interrupt time because it calls the BIOS
*/
void
pctick()
{
long t;
static long oldt; /* Value of bioscnt() on last call */
static long days; /* # of times bioscnt() has rolled over */
/* Update the time-since-boot */
t = bioscnt();
if(t < oldt)
days++; /* bioscnt has rolled past midnight */
oldt = t;
Clock = (days * 0x1800b0) + t - Starttime;
}
/* Set bit(s) in I/O port */
void
setbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)inportb(port)|bits);
}
/* Clear bit(s) in I/O port */
void
clrbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)(inportb(port) & ~bits));
}
/* Set or clear selected bit(s) in I/O port */
void
writebit(port,mask,val)
unsigned port;
char mask;
int val;
{
register char x;
x = inportb(port);
if(val)
x |= mask;
else
x &= ~mask;
outportb(port,x);
}
/* Convert a pointer to a long integer */
long
ptol(p)
void *p;
{
long x;
x = FP_OFF(p);
#ifdef LARGEDATA
x |= (long)FP_SEG(p) << 16;
#endif
return x;
}
void *
ltop(l)
long l;
{
register unsigned seg,offset;
seg = l >> 16;
offset = l;
return MK_FP(seg,offset);
}
#ifdef notdef /* Assembler versions in pcgen.asm */
/* Multiply a 16-bit multiplier by an arbitrary length multiplicand.
* Product is left in place of the multiplicand, and the carry is
* returned
*/
int16
longmul(multiplier,n,multiplicand)
int16 multiplier;
int n; /* Number of words in multiplicand[] */
register int16 *multiplicand; /* High word is in multiplicand[0] */
{
register int i;
unsigned long pc;
int16 carry;
carry = 0;
multiplicand += n;
for(i=n;i != 0;i--){
multiplicand--;
pc = carry + (unsigned long)multiplier * *multiplicand;
*multiplicand = pc;
carry = pc >> 16;
}
}
return carry;
}
/* Divide a 16-bit divisor into an arbitrary length dividend using
* long division. The quotient is returned in place of the dividend,
* and the function returns the remainder.
*/
int16
longdiv(divisor,n,dividend)
int16 divisor;
int n; /* Number of words in dividend[] */
register int16 *dividend; /* High word is in dividend[0] */
{
/* Before each division, remquot contains the 32-bit dividend for this
* step, consisting of the 16-bit remainder from the previous division
* in the high word plus the current 16-bit dividend word in the low
* word.
*
* Immediately after the division, remquot contains the quotient
* in the low word and the remainder in the high word (which is
* exactly where we need it for the next division).
*/
unsigned long remquot;
register int i;
if(divisor == 0)
return 0; /* Avoid divide-by-zero crash */
remquot = 0;
for(i=0;i<n;i++,dividend++){
remquot |= *dividend;
if(remquot == 0)
continue; /* Avoid unnecessary division */
#ifdef __TURBOC__
/* Use assembly lang routine that returns both quotient
* and remainder, avoiding a second costly division
*/
remquot = divrem(remquot,divisor);
*dividend = remquot; /* Extract quotient in low word */
remquot &= ~0xffffL; /* ... and mask it off */
#else
*dividend = remquot / divisor;
remquot = (remquot % divisor) << 16;
#endif
}
return remquot >> 16;
}
#endif
void
sysreset()
{
void (*foo) __ARGS((void));
foo = MK_FP(0xffff,0); /* FFFF:0000 is hardware reset vector */
(*foo)();
}
void
newscreen(sp)
struct session *sp;
{
if(sp != NULLSESSION)
sp->screen = callocw(1,sizeof(struct screen));
}
void
freescreen(sp)
struct session *sp;
{
if(sp == NULLSESSION || sp->screen == NULLSCREEN)
return;
if(sp->screen->save != NULLCHAR)
free(sp->screen->save);
free((char *)sp->screen);
}
/* Save specified session screen and resume console screen */
void
swapscreen(old,new)
struct session *old,*new;
{
struct text_info tr;
if(old == new)
return; /* Nothing to do */
fflush(Rawterm);
gettextinfo(&tr);
if(old != NULLSESSION){
/* Save old screen */
if(old->screen->save == NULLCHAR)
old->screen->save
= malloc(2*tr.screenheight*tr.screenwidth);
if(old->screen->save != NULLCHAR){
if(old->split){
window(1,1,80,25);
tr.winbottom = 25;
}
gettext(tr.winleft,tr.wintop,tr.winright,
tr.winbottom,old->screen->save);
}
old->screen->row = tr.cury;
old->screen->col = tr.curx;
}
if(new != NULLSESSION){
/* Load new screen */
if(new->screen->save != NULLCHAR){
if(new->split){
window(1,1,80,23);
clrscr();
} else {
window(1,1,80,25);
clrscr();
}
puttext(tr.winleft,tr.wintop,tr.winright,
tr.winbottom,new->screen->save);
gotoxy(new->screen->col,new->screen->row);
/* Free the memory (saves 4K on a continuous basis) */
free(new->screen->save);
new->screen->save = NULLCHAR;
} else {
clrscr(); /* Start with a fresh slate */
if(new->split){
new->tsavex = 1;
new->tsavey = 1;
new->bsavex = 1;
new->bsavey = 24;
window(1,1,80,25);
cputs("_\b");
window(1,1,80,23);
}
}
}
alert(Display,1); /* Wake him up */
}
void
display(i,v1,v2)
int i;
void *v1;
void *v2;
{
int c;
struct session *sp;
/* This is very tricky code. Because the value of "Current" can
* change any time we do a pwait, we have to be careful to detect
* any change and go back and start again.
*/
for(;;){
sp = Current;
if(sp->morewait){
pwait(&sp->row);
if(sp != Current || sp->row <= 0){
/* Current changed value, or the user
* hasn't really hit a key
*/
continue;
}
/* Erase the prompt */
fprintf(Rawterm,"\r \r");
}
sp->morewait = 0;
if((c = rrecvchar(sp->output)) == -1){
/* the alert() in swapscreen will cause this to
* return -1 when current changes
*/
pwait(NULL); /* Prevent a nasty loop */
continue;
}
if(sp->split){
if(c == 0x0a){
cputs(Eol);
clreol();
} else
putch(c);
} else {
putc(c,Rawterm);
}
if(sp->record != NULLFILE)
putc(c,sp->record);
if(sp->flowmode && c == '\n' && --sp->row <= 0){
fprintf(Rawterm,"--More--");
sp->morewait = 1;
}
}
}
/* Return time since startup in milliseconds. If the system has an
* 8254 clock chip (standard on ATs and up) then resolution is improved
* below 55 ms (the clock tick interval) by reading back the instantaneous
* value of the counter and combining it with the global clock tick counter.
* Otherwise 55 ms resolution is provided.
*
* Reading the 8254 is a bit tricky since a tick could occur asynchronously
* between the two reads. The tick counter is examined before and after the
* hardware counter is read. If the tick counter changes, try again.
* Note: the hardware counter counts down from 65536.
*/
int32
msclock()
{
int32 hi;
int16 lo;
int16 count[4]; /* extended (48-bit) counter of timer clocks */
if(!Isat)
return Clock * MSPTICK;
do {
hi = Clock + Tick;
lo = clockbits();
} while(hi != Clock + Tick); /* Make sure a tick didn't just occur */
count[0] = 0;
count[1] = hi >> 16;
count[2] = hi;
count[3] = -lo;
longmul(11,4,count); /* The ratio 11/13125 is exact */
longdiv(13125,4,count);
return ((long)count[2] << 16) + count[3];
}
/* Return clock in seconds */
int32
secclock()
{
int32 hi;
int16 lo;
int16 count[4]; /* extended (48-bit) counter of timer clocks */
if(!Isat)
return Clock * MSPTICK / 1000L;
do {
hi = Clock + Tick;
lo = clockbits();
} while(hi != Clock + Tick); /* Make sure a tick didn't just occur */
count[0] = 0;
count[1] = hi >> 16;
count[2] = hi;
count[3] = -lo;
longmul(11,4,count); /* The ratio 11/13125 is exact */
longdiv(13125,4,count);
longdiv(1000,4,count);
return ((long)count[2] << 16) + count[3];
}
int
doisat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setbool(&Isat,"AT/386 mode",argc,argv);
}
/* Directly read BIOS count of time ticks. This is used instead of
* calling biostime(0,0L). The latter calls BIOS INT 1A, AH=0,
* wich resets the midnight overflow flag, losing days on the clock.
*/
long
bioscnt()
{
int i_state;
long rval;
i_state = dirps();
rval = * (long far *)MK_FP(0x40,0x6c);
restore(i_state);
return rval;
}
/* same as getenv(), but return "" instead of NULL when it does not exist */
char *getnenv (name)
char *name;
{
char *rv;
if ((rv = getenv(name)) == NULL)
rv = ""; /* NULL replaced by "" */
return rv;
}